package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math/big"
	"strconv"

	"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

// SmartContract provides functions for transferring tokens between accounts
type SmartContract struct {
	contractapi.Contract
}

//定义基础的链码响应消息格式
type BasicResponse struct {
	Code string      `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

// 定义常用的错误码
const (
	RECODE_OK                  = "0"
	RECODE_UNKOWNERR           = "999"
	RECODE_USER_ALREADY_EXISTS = "100"
	RECODE_USER_NOT_EXISTS     = "101"
	RECODE_PASSWORD_ERR        = "102"
	RECODE_BALANCE_NOT_ENOUGH  = "103"
	RECODE_TASK_NOT_EXISTS     = "104"
	RECODE_TASK_CANNOT_TAKE    = "105"
	RECODE_TASK_CANNOT_COMMIT  = "106"
	RECODE_TASK_CANNOT_CONFIRM = "107"
	RECODE_CC_PUT_ERR          = "108"
	RECODE_CC_GET_ERR          = "109"
	RECODE_CC_DEL_ERR          = "110"
	RECODE_JSON_ERR            = "111"
	RECODE_AUTH_ERR            = "112"
	RECODE_INVOKE_ERR          = "113"
)

// 定义链码状态
const (
	STATUS_PUBLISH = iota
	STATUS_TAKE
	STATUS_COMMIT
	STATUS_CONFIRM
)

// 定义任务结构
type FabTask struct {
	ID       int64  `json:"task_id"`
	Desc     string `json:"desc"`
	Issuer   string `json:"issuer"`
	Holder   string `json:"holder"`
	Status   int8   `json:"status"`
	Bonus    int    `json:"bonus"`
	TaskTime int64  `json:"task_time"`
	Comment  string `json:"comment"`
}

// zone的format
const basicZoneKey = "zone%08d"

// 定义当前的task_id值
const taskSequence = "task_sequence"

// 实现task_id获取函数
func newID(ctx contractapi.TransactionContextInterface) (int64, error) {
	current_value, err := ctx.GetStub().GetState(taskSequence)
	if err != nil {
		return -1, err
	}
	val := int64(1)
	if current_value != nil {
		val = big.NewInt(0).SetBytes(current_value).Int64()
	}

	err = ctx.GetStub().PutState(taskSequence, big.NewInt(val+1).Bytes())
	if err != nil {
		return -1, err
	}
	return val, nil
}

// 实现zone获取函数
func getZoneKey(id int64) (string, error) {
	if id < 1 {
		return "", fmt.Errorf("id(%d) is invalid", id)
	}

	basic := id / 100
	return fmt.Sprintf(basicZoneKey, basic), nil
}

// 任务发布
func (s *SmartContract) NewTask(
	ctx contractapi.TransactionContextInterface,
	issuer, desc string,
	bonus int) *BasicResponse {
	// 权限验证 Org1Msp可以操作任务
	resp := BasicResponse{RECODE_OK, "OK", nil}
	// 权限控制
	// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
	clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
	if err != nil {
		resp.Code = RECODE_UNKOWNERR
		resp.Msg = fmt.Errorf("failed to get MSPID: %v", err).Error()
		return &resp
	}
	if clientMSPID != "Org1MSP" {
		resp.Code = RECODE_AUTH_ERR
		resp.Msg = fmt.Errorf("client is not authorized to mint new tokens").Error()
		return &resp
	}
	// 检测用户是否有足够的资金
	response := ctx.GetStub().InvokeChaincode("tokens", [][]byte{[]byte("BalanceOf"), []byte(issuer)}, "ykchannel")
	if response.Status != 200 {
		resp.Code = RECODE_INVOKE_ERR
		resp.Msg = fmt.Errorf("failed to invokechaincode token's balance").Error()
		return &resp
	}
	balance, err := strconv.Atoi(string(response.Payload))
	if err != nil {
		resp.Code = RECODE_UNKOWNERR
		resp.Msg = err.Error()
		return &resp
	}
	if bonus <= 0 || balance < bonus {
		resp.Code = RECODE_BALANCE_NOT_ENOUGH
		resp.Msg = err.Error()
		return &resp
	}

	// 获取一个任务id
	taskid, err := newID(ctx)
	if err != nil {
		resp.Code = RECODE_UNKOWNERR
		resp.Msg = err.Error()
		return &resp
	}

	// 组建一个新任务
	tt, err := ctx.GetStub().GetTxTimestamp()
	if err != nil {
		resp.Code = RECODE_CC_GET_ERR
		resp.Msg = err.Error()
		return &resp
	}
	task := FabTask{
		ID:       taskid,
		Desc:     desc,
		Issuer:   issuer,
		Bonus:    bonus,
		Status:   STATUS_PUBLISH,
		TaskTime: tt.GetSeconds(),
	}
	// 获取zone
	zoneKey, err := getZoneKey(taskid)
	if err != nil {
		resp.Code = RECODE_UNKOWNERR
		resp.Msg = fmt.Errorf("failed to get zone(id=%d)", taskid).Error()
		return &resp
	}
	// 存取
	taskData, err := ctx.GetStub().GetState(zoneKey)
	if err != nil {
		resp.Code = RECODE_CC_GET_ERR
		resp.Msg = err.Error()
		return &resp
	}

	var tasks []FabTask
	if taskData != nil {
		err = json.Unmarshal(taskData, &tasks)
		if err != nil {
			resp.Code = RECODE_JSON_ERR
			resp.Msg = err.Error()
			return &resp
		}
	}
	tasks = append(tasks, task)
	taskData, err = json.Marshal(tasks)
	if err != nil {
		resp.Code = RECODE_JSON_ERR
		resp.Msg = err.Error()
		return &resp
	}
	err = ctx.GetStub().PutState(zoneKey, taskData)
	if err != nil {
		resp.Code = RECODE_CC_PUT_ERR
		resp.Msg = err.Error()
		return &resp
	}
	resp.Data = taskid
	return &resp
}

// 任务接受
func (s *SmartContract) TakeTask(
	ctx contractapi.TransactionContextInterface,
	taskid int64,
	operator string) *BasicResponse {

	// 权限验证 Org1Msp可以操作任务
	resp := &BasicResponse{RECODE_OK, "OK", nil}
	// 权限控制
	// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
	clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
	if err != nil {
		resp.Code = RECODE_UNKOWNERR
		resp.Msg = fmt.Errorf("failed to get MSPID: %v", err).Error()
		return resp
	}
	if clientMSPID != "Org1MSP" {
		resp.Code = RECODE_AUTH_ERR
		resp.Msg = fmt.Errorf("client is not authorized to mint new tokens").Error()
		return resp
	}

	// 找到该任务
	resp = s.ReadTask(ctx, taskid)
	if resp.Code != RECODE_OK {
		return resp
	}
	task := resp.Data.(FabTask)

	// 检测任务是否可以接受
	if task.Status != STATUS_PUBLISH {
		resp.Code = RECODE_TASK_CANNOT_TAKE
		resp.Msg = fmt.Errorf("task's status can not take").Error()
		return resp
	}
	// 修改任务
	return s.updateTask(ctx, taskid, STATUS_TAKE, operator, "")
}

// 任务提交
func (s *SmartContract) CommitTask(
	ctx contractapi.TransactionContextInterface,
	taskid int64,
	operator string) *BasicResponse {

	// 权限验证 Org1Msp可以操作任务
	resp := &BasicResponse{RECODE_OK, "OK", nil}
	// 权限控制
	// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
	clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
	if err != nil {
		resp.Code = RECODE_UNKOWNERR
		resp.Msg = fmt.Errorf("failed to get MSPID: %v", err).Error()
		return resp
	}
	if clientMSPID != "Org1MSP" {
		resp.Code = RECODE_AUTH_ERR
		resp.Msg = fmt.Errorf("client is not authorized to mint new tokens").Error()
		return resp
	}

	// 找到该任务
	resp = s.ReadTask(ctx, taskid)
	if resp.Code != RECODE_OK {
		return resp
	}
	task := resp.Data.(FabTask)

	// 检测任务是否可以接受
	if task.Status != STATUS_TAKE || operator != task.Holder {
		resp.Code = RECODE_TASK_CANNOT_COMMIT
		resp.Msg = fmt.Errorf("task's status can not commit").Error()
		return resp
	}
	// 修改任务
	return s.updateTask(ctx, taskid, STATUS_COMMIT, operator, "")
}

// 任务确认
func (s *SmartContract) ConfirmTask(
	ctx contractapi.TransactionContextInterface,
	taskid int64,
	operator, comment string,
	status int8) *BasicResponse {

	// 权限验证 Org1Msp可以操作任务
	resp := &BasicResponse{RECODE_OK, "OK", nil}
	// 权限控制
	// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
	clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
	if err != nil {
		resp.Code = RECODE_UNKOWNERR
		resp.Msg = fmt.Errorf("failed to get MSPID: %v", err).Error()
		return resp
	}
	if clientMSPID != "Org1MSP" {
		resp.Code = RECODE_AUTH_ERR
		resp.Msg = fmt.Errorf("client is not authorized to mint new tokens").Error()
		return resp
	}

	// 找到该任务
	resp = s.ReadTask(ctx, taskid)
	if resp.Code != RECODE_OK {
		return resp
	}
	task := resp.Data.(FabTask)

	// 检测任务是否可以确认
	if task.Status != STATUS_COMMIT || operator != task.Issuer {
		resp.Code = RECODE_TASK_CANNOT_CONFIRM
		resp.Msg = fmt.Errorf("task's status can not confirm").Error()
		return resp
	}
	// 修改任务
	if status != STATUS_CONFIRM {
		status = STATUS_TAKE
	} else {
		bonus := fmt.Sprintf("%d", task.Bonus)
		response := ctx.GetStub().InvokeChaincode("tokens", [][]byte{[]byte("Transfer"), []byte(task.Issuer), []byte(task.Holder), []byte(bonus)}, ctx.GetStub().GetChannelID())
		if response.Status != 200 {
			resp.Code = RECODE_INVOKE_ERR
			resp.Msg = fmt.Errorf("failed to invokechaincode token's balance").Error()
			return resp
		}
	}
	return s.updateTask(ctx, taskid, status, operator, comment)
}

// 单任务读取
func (s *SmartContract) ReadTask(
	ctx contractapi.TransactionContextInterface,
	taskid int64) *BasicResponse {

	resp := BasicResponse{RECODE_OK, "OK", nil}
	// 找到zone
	zoneKey, err := getZoneKey(taskid)
	if err != nil {
		resp.Code = RECODE_UNKOWNERR
		resp.Msg = err.Error()
		return &resp
	}
	// 在zone中找到任务
	taskData, err := ctx.GetStub().GetState(zoneKey)
	if err != nil {
		resp.Code = RECODE_CC_GET_ERR
		resp.Msg = err.Error()
		return &resp
	}
	// 转换成切片
	var tasks []FabTask
	if taskData != nil {
		err = json.Unmarshal(taskData, &tasks)
		if err != nil {
			resp.Code = RECODE_JSON_ERR
			resp.Msg = err.Error()
			return &resp
		}
	}
	for _, v := range tasks {
		if v.ID == taskid {
			// do str return
			resp.Data = v
			return &resp
		}
	}

	resp.Code = RECODE_TASK_NOT_EXISTS
	return &resp
}

// 修改任务
func (s *SmartContract) updateTask(
	ctx contractapi.TransactionContextInterface,
	taskid int64,
	status int8,
	operator string,
	comment string) *BasicResponse {

	resp := BasicResponse{RECODE_OK, "OK", nil}
	// 找到zone
	zoneKey, err := getZoneKey(taskid)
	if err != nil {
		resp.Code = RECODE_UNKOWNERR
		resp.Msg = err.Error()
		return &resp
	}
	// 在zone中找到任务
	taskData, err := ctx.GetStub().GetState(zoneKey)
	if err != nil {
		resp.Code = RECODE_CC_GET_ERR
		resp.Msg = err.Error()
		return &resp
	}
	// 转换成切片
	var tasks []FabTask
	if taskData != nil {
		err = json.Unmarshal(taskData, &tasks)
		if err != nil {
			resp.Code = RECODE_JSON_ERR
			resp.Msg = err.Error()
			return &resp
		}
	}
	for k, v := range tasks {
		if v.ID == taskid {
			if operator != "" {
				tasks[k].Holder = operator
			}

			tasks[k].Status = status
			if comment != "" {
				tasks[k].Comment = comment
			}
			resp.Data = tasks[k]

			taskData, err = json.Marshal(tasks)
			if err != nil {
				resp.Code = RECODE_JSON_ERR
				resp.Msg = err.Error()
				return &resp
			}
			err = ctx.GetStub().PutState(zoneKey, taskData)
			if err != nil {
				resp.Code = RECODE_CC_PUT_ERR
				resp.Msg = err.Error()
				return &resp
			}

			return &resp
		}
	}

	resp.Code = RECODE_TASK_NOT_EXISTS
	return &resp
}

// 任务列表读取
func (s *SmartContract) GetAllTask(
	ctx contractapi.TransactionContextInterface,
	page int) *BasicResponse {

	resp := BasicResponse{RECODE_OK, "OK", nil}
	// 找到zone
	/*
		1  0  ~ 9  zone00000000
		2  10 ~ 19 zone00000000
		10 90 ~ 99 zone00000000
		11 100~109 zone00000001
	*/
	if page < 1 {
		page = 1
	}
	taskid := int64((page-1)*10 + 5)
	zoneKey, err := getZoneKey(taskid)
	if err != nil {
		resp.Code = RECODE_UNKOWNERR
		resp.Msg = err.Error()
		return &resp
	}
	// 在zone中找到任务
	taskData, err := ctx.GetStub().GetState(zoneKey)
	if err != nil {
		resp.Code = RECODE_CC_GET_ERR
		resp.Msg = err.Error()
		return &resp
	}
	// 转换成切片
	var tasks []FabTask
	if taskData != nil {
		err = json.Unmarshal(taskData, &tasks)
		if err != nil {
			resp.Code = RECODE_JSON_ERR
			resp.Msg = err.Error()
			return &resp
		}
	}
	page = page % 10
	if page-len(tasks)/10 > 1 {
		page = len(tasks) + 1
	}
	//ipage := len(tasks) / 10
	ibegin := page*10 - 10
	iend := page * 10
	if iend > len(tasks) {
		iend = len(tasks)
	}

	resp.Data = tasks[ibegin:iend]
	return &resp
}

func main() {
	taskChaincode, err := contractapi.NewChaincode(&SmartContract{})
	if err != nil {
		log.Panicf("Error creating tasks chaincode: %v", err)
	}

	if err := taskChaincode.Start(); err != nil {
		log.Panicf("Error starting tasks chaincode: %v", err)
	}
}
